package cn.chenfeifx.core.controls;

import cn.chenfeifx.core.common.ClipboardUtils;
import cn.chenfeifx.core.common.FxUtil;
import cn.chenfeifx.core.common.ResourcesLoader;
import cn.chenfeifx.core.common.SimpleControl;
import javafx.animation.*;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.ToggleButton;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
import org.kordamp.ikonli.antdesignicons.AntDesignIconsOutlined;
import org.kordamp.ikonli.javafx.FontIcon;

import java.util.List;


/**
 * @author ChenFei
 * @date 2023/5/14
 */
public class CFImageViewer extends CFStage {

    private static final String STYLE_SHEET = ResourcesLoader.load("/cn/chenfeifx/core/css/cf-image-viewer.css");
    private static final CFImageViewer IMAGE_VIEWER = new CFImageViewer();
    private final ObjectProperty<List<Image>> images = new SimpleObjectProperty<>();
    // buttons
    private final Button rotateLeft = getIconButton(FontIcon.of(AntDesignIconsOutlined.UNDO), "左旋转");// UNDO
    private final Button rotateRight = getIconButton(FontIcon.of(AntDesignIconsOutlined.REDO), "右旋转");// REDO
    // ONE_TO_ONE ;EXPAND
    private final ToggleButton expand = getIconToggleButton(FontIcon.of(AntDesignIconsOutlined.EXPAND));
    //private final Button PLAY_CIRCLE = getIconButton(FontIcon.of(AntDesignIconsOutlined.PLAY_CIRCLE), "自动播放");
    private final Button zoomIn = getIconButton(FontIcon.of(AntDesignIconsOutlined.ZOOM_IN), "放大");
    private final Button zoomOut = getIconButton(FontIcon.of(AntDesignIconsOutlined.ZOOM_OUT), "缩小");
    private final HBox buttonBox = new HBox(rotateLeft, rotateRight, expand, zoomIn, zoomOut);
    private final Button previous = getIconButton(FontIcon.of(AntDesignIconsOutlined.LEFT), "上一个");
    private final Button next = getIconButton(FontIcon.of(AntDesignIconsOutlined.RIGHT), "下一个");
    //
    private final ImageView imageView = new ImageView();
    private final StackPane rotatePane = new StackPane(imageView);
    private final StackPane container = new StackPane(rotatePane, buttonBox, previous, next);
    //
    private final Hyperlink hyperlinkTitle = SimpleControl.getHyperlink("");

    private CFImageViewer() {
        setContent(container);
        initialize();
    }

    public static CFImageViewer getInstance() {
        return IMAGE_VIEWER;
    }

    public void setImageList(List<Image> images) {
        setImageList(images, 0);
    }

    public void setImageList(List<Image> images, int index) {
        this.images.set(images);
        Image image = this.images.get().get(index);
        this.imageView.setImage(image);
    }

    private void initialize() {
        FxUtil.clipRect(container, new SimpleDoubleProperty(0));
        buttonBox.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
        StackPane.setAlignment(buttonBox, Pos.BOTTOM_CENTER);
        StackPane.setMargin(buttonBox, new Insets(20));
        StackPane.setAlignment(previous, Pos.CENTER_LEFT);
        StackPane.setMargin(previous, new Insets(20));
        StackPane.setAlignment(next, Pos.CENTER_RIGHT);
        StackPane.setMargin(next, new Insets(20));
        //
        setHeaderTitle(hyperlinkTitle);
        imageView.setPreserveRatio(true);
        imageView.setSmooth(true);
        imageView.setManaged(false);
        //
        container.getStyleClass().add("cf-image-viewer");
        buttonBox.getStyleClass().add("button-box");
        previous.getStyleClass().add("left");
        next.getStyleClass().add("right");
        container.getStylesheets().add(STYLE_SHEET);
        initEvent();
        setOnShown(event -> oneToOne(false));
    }

    // 旋转 rotate
    private final Timeline rotateTimeline = new Timeline();
    private final Rotate ROTATE = new Rotate(0, Rotate.Z_AXIS); // Z轴旋转
    // 缩放 scale
    private final Timeline scaleTimeline = new Timeline();

    private void initEvent() {
        // 图片位置绑定
        imageView.layoutXProperty().bind(Bindings
                .createDoubleBinding(() -> {
                            Bounds layoutBounds = imageView.getLayoutBounds();
                            return (container.getWidth() - layoutBounds.getWidth()) / 2;
                        },
                        container.widthProperty(),
                        imageView.layoutBoundsProperty()));
        imageView.layoutYProperty().bind(Bindings
                .createDoubleBinding(() -> {
                            Bounds layoutBounds = imageView.getLayoutBounds();
                            return (container.getHeight() - layoutBounds.getHeight()) / 2;
                        },
                        container.heightProperty(),
                        imageView.layoutBoundsProperty()));
        /* 旋转效果 */
        rotatePane.getTransforms().add(ROTATE);
        rotateLeft.setOnMouseClicked(event -> rotateImage(-90));
        rotateRight.setOnMouseClicked(event -> rotateImage(90));
        /* 放大缩小效果 */
        zoomIn.setOnMouseClicked(event -> imageScale(true, true));
        zoomOut.setOnMouseClicked(event -> imageScale(false, true));
        getScene().addEventHandler(ScrollEvent.SCROLL, se -> imageScale(se.getDeltaY() > 0, false));// 监听滚轮
        /* 自适应；1:1 效果 */
        expand.graphicProperty().bind(Bindings.createObjectBinding(() ->
                        FontIcon.of(expand.isSelected() ? AntDesignIconsOutlined.EXPAND : AntDesignIconsOutlined.ONE_TO_ONE),
                expand.selectedProperty()));
        expand.tooltipProperty().bind(Bindings.createObjectBinding(() ->
                        SimpleControl.getTooltip(expand.isSelected() ? "自适应大小" : "图片原始大小"),
                expand.selectedProperty()));
        expand.selectedProperty().addListener((observable, oldValue, newValue) -> oneToOne(newValue));
        /* 上一个；下一个 */
        next.setOnMouseClicked(event -> next());
        previous.setOnMouseClicked(event -> previous());
        getScene().addEventHandler(KeyEvent.ANY, se -> { //键盘事件
            switch (se.getCode()) {
                case RIGHT -> next();
                case LEFT -> previous();
            }
        });
        // 图片拖动
        imageViewMove();
        // 标题
        imageView.imageProperty().addListener((observable, oldValue, newValue) -> {
            String url = newValue.getUrl();
            if (newValue.isError()) {
                url = "";
            } else {
                oneToOne(false);
            }
            setTitle(url);
            hyperlinkTitle.setText(url);
        });
        // 点击标题复制文本
        hyperlinkTitle.setOnMouseClicked(event -> ClipboardUtils.setString(hyperlinkTitle.getText()));
        // 是否显示上一个下一个按钮
        images.addListener((observable, oldValue, newValue) -> {
            boolean b = newValue.size() > 1;
            next.setManaged(b);
            next.setVisible(b);
            previous.setManaged(b);
            previous.setVisible(b);
        });
    }

    /**
     * 旋转图片
     *
     * @param val ： 角度
     */
    private void rotateImage(double val) {
        if (Animation.Status.RUNNING.equals(rotateTimeline.getStatus())) {
            return;
        }
        //旋转之前重新定位：
        imageView.setX(0);
        imageView.setY(0);
        Bounds boundsInParent = imageView.getBoundsInParent();
        ROTATE.setPivotX(boundsInParent.getCenterX());
        ROTATE.setPivotY(boundsInParent.getCenterY());
        KeyFrame keyFrame1 = new KeyFrame(Duration.millis(0),
                new KeyValue(ROTATE.angleProperty(), ROTATE.getAngle(), Interpolator.EASE_BOTH)
        );
        KeyFrame keyFrame2 = new KeyFrame(Duration.millis(250),
                new KeyValue(ROTATE.angleProperty(), ROTATE.getAngle() + val, Interpolator.EASE_BOTH)
        );
        rotateTimeline.getKeyFrames().setAll(keyFrame1, keyFrame2);
        rotateTimeline.play();
    }

    /**
     * 图片缩放
     *
     * @param val       true : 放大 ；false：缩小
     * @param animation true : 使用动画；false：不使用动画
     */
    private void imageScale(boolean val, boolean animation) {
        if (Animation.Status.RUNNING.equals(scaleTimeline.getStatus())) {
            return;
        }
        double fitWidth = imageView.getFitWidth();
        double fitHeight = imageView.getFitHeight();
        double toFitWidth = val ? fitWidth / 0.8 : fitWidth * 0.8;
        double toFitHeight = val ? fitHeight / 0.8 : fitHeight * 0.8;
        if (animation) {
            KeyFrame keyFrame1 = new KeyFrame(Duration.millis(0),
                    new KeyValue(imageView.fitWidthProperty(), fitWidth, Interpolator.EASE_BOTH),
                    new KeyValue(imageView.fitHeightProperty(), fitHeight, Interpolator.EASE_BOTH)
            );
            KeyFrame keyFrame2 = new KeyFrame(Duration.millis(250),
                    new KeyValue(imageView.fitWidthProperty(), toFitWidth, Interpolator.EASE_BOTH),
                    new KeyValue(imageView.fitHeightProperty(), toFitHeight, Interpolator.EASE_BOTH)
            );
            scaleTimeline.getKeyFrames().setAll(keyFrame1, keyFrame2);
            scaleTimeline.play();
        } else {
            imageView.setFitWidth(toFitWidth);
            imageView.setFitHeight(toFitHeight);
        }
    }

    /**
     * 下一个
     */
    private void next() {
        List<Image> imageList = images.get();
        int i = imageList.indexOf(imageView.getImage()) + 1;
        if (i >= imageList.size()) {
            imageView.setImage(imageList.get(0));
        } else {
            imageView.setImage(imageList.get(i));
        }
    }

    /**
     * 上一个
     */
    private void previous() {
        List<Image> imageList = images.get();
        int i = imageList.indexOf(imageView.getImage()) - 1;
        if (i < 0) {
            imageView.setImage(imageList.get(imageList.size() - 1));
        } else {
            imageView.setImage(imageList.get(i));
        }
    }

    private void oneToOne(boolean val) {
        expand.setSelected(val);
        Image image = imageView.getImage();
        if (val) { // 1:1
            imageView.setFitWidth(image.getWidth());
            imageView.setFitHeight(image.getHeight());
        } else { // 自适应窗口
            double height = container.getHeight();
            double width = container.getWidth();
            if (image.getWidth() < width && image.getHeight() < height) {
                imageView.setFitWidth(image.getWidth());
                imageView.setFitHeight(image.getHeight());
            } else {
                imageView.setFitWidth(width);
                imageView.setFitHeight(height);
            }
        }
        imageView.setX(0);
        imageView.setY(0);
        ROTATE.setAngle(0);
    }

    private Button getIconButton(FontIcon fontIcon, String tip) {
        Button button = new Button();
        button.setGraphic(fontIcon);
        button.setTooltip(SimpleControl.getTooltip(tip));
        button.getStyleClass().add("icon-button");
        return button;
    }

    private ToggleButton getIconToggleButton(FontIcon fontIcon) {
        ToggleButton button = new ToggleButton();
        button.setGraphic(fontIcon);
        button.getStyleClass().add("icon-button");
        return button;
    }


    private double xOffset, yOffset;

    private void imageViewMove() {
        // 拖拽图标
        this.imageView.cursorProperty().bind(Bindings
                .createObjectBinding(() -> imageView.isPressed() ? Cursor.MOVE : Cursor.DEFAULT,
                        this.imageView.pressedProperty()));
        // 按下
        this.imageView.setOnMousePressed(event -> {
            event.consume();
            this.xOffset = this.imageView.getX() - event.getX();
            this.yOffset = this.imageView.getY() - event.getY();
        });
        this.imageView.setOnMouseDragged(event -> {
            event.consume();
            this.imageView.setX(event.getX() + this.xOffset);
            this.imageView.setY(event.getY() + this.yOffset);
        });
    }

}
